package com.xckj.cycling.view

import android.animation.ValueAnimator
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Rect
import android.location.Location
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.PixelCopy
import android.view.View
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import com.luck.picture.lib.photoview.OnScaleChangedListener
import com.mapbox.geojson.BoundingBox
import com.mapbox.geojson.Feature
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.GeoJson
import com.mapbox.geojson.Geometry
import com.mapbox.geojson.GeometryCollection
import com.mapbox.geojson.LineString
import com.mapbox.geojson.MultiPoint
import com.mapbox.geojson.Point
import com.mapbox.geojson.gson.GeometryGeoJson
import com.mapbox.maps.*
import com.mapbox.maps.dsl.cameraOptions
import com.mapbox.maps.extension.style.sources.generated.GeoJsonSource
import com.mapbox.maps.plugin.animation.MapAnimationOptions
import com.mapbox.maps.plugin.animation.camera
import com.mapbox.maps.plugin.gestures.addOnScaleListener
import com.mapbox.maps.plugin.locationcomponent.*
import com.mapbox.navigation.base.route.NavigationRoute
import com.mapbox.navigation.base.trip.model.RouteProgress
import com.mapbox.navigation.base.trip.model.RouteProgressState
import com.mapbox.navigation.core.trip.session.LocationMatcherResult
import com.mapbox.navigation.core.trip.session.LocationObserver
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
import com.mapbox.navigation.ui.maneuver.model.Maneuver
import com.mapbox.navigation.ui.maps.camera.data.MapboxNavigationViewportDataSource
import com.xckj.cycling.R
import com.xckj.cycling.activity.NavigationDetailActivity
import com.xckj.cycling.databinding.LayoutNavigationInfomationViewBinding
import com.xckj.cycling.dialog.StopNavigationDialog
import com.xckj.cycling.framework.BaseActivityKt
import com.xckj.cycling.model.Route
import com.xckj.cycling.model.RoutePoint
import com.xckj.cycling.service.CyclingService
import com.xckj.cycling.utils.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.math.min
import kotlin.time.toDuration

class NavigationMapView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
    RouteMapView(context, attrs) {
    private var test = false//是否测试模式
    private val mapUtils: MapUtils = MapUtils(this)
    private var lastJob: Job? = null
    private var paused = false
    private var start = false

    fun setInfoView(infoView: LayoutNavigationInfomationViewBinding) {
        this.infoView = infoView
        infoView.apply {
            stop.setOnClickListener {
                if (!start) {
                    return@setOnClickListener
                }
                if (viewModel.cid == null) {
                    //还没正式开始
                    return@setOnClickListener
                }
                StopNavigationDialog(context, this@NavigationMapView).show()
            }
            pause.setOnClickListener {
                if (!start) {
                    start = true
                    val c = context
                    if (c is BaseActivityKt) {
                        c.showLoading()
                        viewModel.start {
                            stop.visibility = View.VISIBLE
                            updateZoom(17.0, lastBearing)
                            updateIcon(true)
                            CyclingService.start()
                            lastPoint?.let {
                                mapUtils.addAnnotationToMap(it, R.mipmap.ic_start_route, true)
                                updateIcon(isInNavationMode)
                            }
                            mapUtils.addLineLayer()
                            c.hideDialog()
                            last = System.currentTimeMillis()
                            time = 0
                            pause.setImageResource(R.mipmap.ic_navigation_pause)
                            val l: LifecycleOwner = context as LifecycleOwner
                            lastJob = l.lifecycleScope.launch {
                                while (isActive) {
                                    val diff = System.currentTimeMillis() - last
                                    last = System.currentTimeMillis()
                                    if (!paused) {
                                        time += diff
                                        totalTime.text = time.toFormattedTime()
                                    }
                                    if (test) {
                                        lastPoint?.apply {
                                            mapUtils.addPointToLine(this)
                                        }
                                    }
                                    delay(1000)
                                }
                            }
                        }

                    }


                    return@setOnClickListener
                }
                if (viewModel.cid == null) {
                    //开始接口还没调用成功
                    return@setOnClickListener
                }
                if (paused) {
                    viewModel.resume()
                } else {
                    viewModel.pause()
                }
                paused = !paused
                when {
                    paused -> {
                        pause.setImageResource(R.mipmap.ic_navigation_go_on)
                    }

                    else -> {
                        pause.setImageResource(R.mipmap.ic_navigation_pause)
                    }
                }
            }
        }

    }

    private lateinit var infoView: LayoutNavigationInfomationViewBinding
    private var isInNavationMode = false
    fun updateIcon(navigation: Boolean) {
        updateSetting(location, navigation)
        isInNavationMode = navigation
    }

    fun stop() {
        if (!start) {
            return
        }
        captureing = true
        moveEndCamera()
        if (time < 30 * 1000) {//不足30s 直接结束，不存数据
            captureing = false
            over()
            updateIcon(false)
            infoView.stop.visibility = View.GONE
            CyclingService.stop()
            mapUtils.clearAnnotation()
            navigation.setNavigationRoutes(emptyList())
            getMapboxMap().getStyle() { style ->
                //删除所有 导航相关的layer
                style.styleLayers.filter {
                    it.id.contains("navigation")
                }.map {
                    it.id
                }.forEach {
                    style.removeStyleLayer(it)
                }
                routeLineApi.clearRouteLine() {
                    routeLineView.renderClearRouteLineValue(style, it)
                    routeLineView.hideAlternativeRoutes(style)
                }
            }
            return
        }
        val a = context as BaseActivityKt
        lastPoint?.let {
            mapUtils.addAnnotationToMap(
                Point.fromLngLat(
                    it.longitude(),
                    it.latitude() + 0.0005 //加一点，不然重叠了看不出来
                ), R.mipmap.ic_end_route, false
            )
            invalidate()

        }
        updateIcon(false)
        paused = false
        postDelayed({
            val rect = Rect()
            val location = IntArray(2)
            this.getLocationInWindow(location)
            rect.bottom = location[1] + height
            rect.right = width
            val bitmap = Bitmap.createBitmap(
                width,
                rect.height(),
                Bitmap.Config.ARGB_8888
            )
            PixelCopy.request(
                a.window,
                rect,
                bitmap,
                object : PixelCopy.OnPixelCopyFinishedListener {
                    override fun onPixelCopyFinished(copyResult: Int) {
                        a.showLoading()
                        viewModel.stop(bitmap) {
                            captureing = false
                            infoView.stop.visibility = View.GONE
                            updateZoom(12.0, lastBearing)
                            CyclingService.stop()
                            a.hideDialog()
                            context.startActivity(
                                Intent(
                                    context,
                                    NavigationDetailActivity::class.java
                                )
                            )
                            mapUtils.clearAnnotation()
                            navigation.setNavigationRoutes(emptyList())

                            getMapboxMap().getStyle() { style ->
                                //删除所有 导航相关的layer
                                style.styleLayers.filter {
                                    it.id.contains("navigation")
                                }.map {
                                    it.id
                                }.forEach {
                                    style.removeStyleLayer(it)
                                }
                                routeLineApi.clearRouteLine() {
                                    routeLineView.renderClearRouteLineValue(style, it)
                                    routeLineView.hideAlternativeRoutes(style)
                                }
                            }
                        }
                    }

                },
                Handler(Looper.getMainLooper())
            )
        }, 60) //等待50ms 等待结束的marker 渲染

        over()
    }

    // 保证路线能显示到屏幕上
    private fun moveEndCamera() {
        val edge = DensityUtil.dip2px(40f).toDouble()
        val options = getMapboxMap().cameraForGeometry(
            mapUtils.getLineString(),
            EdgeInsets(
                edge,
                edge,
                edge,
                edge
            )
        )
        val o = options.toBuilder().bearing(0.0)
            .zoom(Math.min(getMapboxMap().cameraState.zoom, options.zoom ?: 1000.0).also {
                this.zoom = it
            }).build()
        camera.easeTo(o, MapAnimationOptions.mapAnimationOptions {
            duration(0)
        })
    }

    fun over() {
        start = false
        infoView.pause.setImageResource(R.mipmap.ic_navigation_go_on)
        lastJob?.cancel()
        infoView.totalTime.text = "--:--:--"
        infoView.totalKaluli.text = "0kcal"
        infoView.totalDistance.text = "0.0miles"
        infoView.totalAtt.text = "0.0"
    }

    companion object {
        private const val TAG = "NavigationMapView"
    }

    /**
     * Produces the camera frames based on the location and routing data for the [navigationCamera] to execute.
     */
    private lateinit var viewportDataSource: MapboxNavigationViewportDataSource

    private var complete = false
    private val progressObserver: RouteProgressObserver = object : RouteProgressObserver {
        override fun onRouteProgressChanged(routeProgress: RouteProgress) {
            val maneuvers = maneuverApi.getManeuvers(routeProgress)
            val m = maneuvers.value
            bind(m)
            if (routeProgress.currentState == RouteProgressState.COMPLETE) {
                if (complete) {
                    return
                }
                complete = true
                mapboxNavigation.stopTripSession()
                stop()
                context.startActivity(Intent(context, NavigationDetailActivity::class.java))
            }
        }

    }


    override fun onMapReady() {
        infoView.root.bringToFront()
        viewportDataSource = MapboxNavigationViewportDataSource(getMapboxMap())
        val provider = DefaultLocationProvider(context)
        location.setLocationProvider(provider)
        provider.registerLocationConsumer(object : LocationConsumer2 {
            override fun onAccuracyRadiusUpdated(
                vararg radius: Double,
                options: (ValueAnimator.() -> Unit)?
            ) {
                viewModel.accuracy = radius[0]
            }

            override fun onBearingUpdated(
                vararg bearing: Double,
                options: (ValueAnimator.() -> Unit)?
            ) {
                lastBearing = bearing[0]
            }

            override fun onLocationUpdated(
                vararg location: Point,
                options: (ValueAnimator.() -> Unit)?
            ) {
                viewModel.locationChanged(location[0])
                if (start) {
                    mapUtils.addPointToLine(location[0])
                }
            }

            override fun onPuckAccuracyRadiusAnimatorDefaultOptionsUpdated(options: ValueAnimator.() -> Unit) {
            }

            override fun onPuckBearingAnimatorDefaultOptionsUpdated(options: ValueAnimator.() -> Unit) {
            }

            override fun onPuckLocationAnimatorDefaultOptionsUpdated(options: ValueAnimator.() -> Unit) {
            }

        })
        super.onMapReady()

    }

    private val locationObserver: LocationObserver = object : LocationObserver {
        var firstLocationUpdateReceived = false

        override fun onNewLocationMatcherResult(locationMatcherResult: LocationMatcherResult) {
            val enhancedLocation = locationMatcherResult.enhancedLocation
//            appendLog("地址更新：" + enhancedLocation.toString())
            // update camera position to account for new location
            viewportDataSource.onLocationChanged(enhancedLocation)
            viewportDataSource.evaluate()

            // if this is the first location update the activity has received,
            // it's best to immediately move the camera to the current user location
            if (!firstLocationUpdateReceived) {
                firstLocationUpdateReceived = true
//                navigationCamera.requestNavigationCameraToOverview(
//                    stateTransitionOptions = NavigationCameraTransitionOptions.Builder()
//                        .maxDuration(0) // instant transition
//                        .build()
//                )
            }
        }

        override fun onNewRawLocation(rawLocation: Location) {
        }
    }

    val navigation = mapboxNavigation
    private var last = System.currentTimeMillis()
    private var time = 0L
    override fun onCreate(owner: LifecycleOwner) {
        super.onCreate(owner)
        navigation.registerRouteProgressObserver(progressObserver)
        navigation.registerLocationObserver(locationObserver)

        viewModel.altitudeLiveData.observe(owner) {
            infoView.totalAtt.text = it.toReadable()
        }
        viewModel.heartBeatLiveData.observe(owner) {
            if (it != null) {
                infoView.totalKaluli.text = it.calories.toReadable() + "kcal"
                infoView.totalDistance.text = it.distance
                infoView.totalAtt.text = it.totalalt
            }
        }
        viewModel.pointsLiveData.observe(owner) {
            drawPoints(it)
        }
        viewModel.lineStringLiveData.observe(owner) {
            drawLineString(it, lastBearing)
        }
    }

    private fun drawPoints(list: MutableList<RoutePoint>) {
        mapUtils.drawPoints(list)
        mapUtils.drawLineString(LineString.fromLngLats(emptyList()))
        if (!list.isNullOrEmpty()) {
            updateIcon(true)
        }
    }

    private fun drawLineString(route: Route, bear: Double?) {
        val lineString = route.lineString
        if (!lineString.coordinates().isNullOrEmpty()) {
            updateIcon(true)
        }
        mapUtils.drawLineString(lineString)
        mapUtils.drawPoints(arrayListOf())
        updateZoom(12.0, bear)
        mapUtils.addAnnotationToMap(route.lineStart, R.mipmap.ic_line_start, clearBefore = true)
        mapUtils.addAnnotationToMap(
            route.end, R.mipmap.ic_end_route, false
        )
    }

    override fun onStyleLoad(style: Style) {
        super.onStyleLoad(style)
        viewModel.pointsLiveData.value?.let {
            drawPoints(it)
            updateCamera(it.first().let { Point.fromLngLat(it.lng, it.lat) })
        }
        viewModel.lineStringLiveData.value?.let {
            drawLineString(it, lastBearing)
            updateCamera(it.lineString.coordinates().first())
        }
    }


    override fun onDestroy(owner: LifecycleOwner) {
        super.onDestroy(owner)
        navigation.unregisterRouteProgressObserver(progressObserver)
        navigation.unregisterLocationObserver(locationObserver)
        navigation.stopTripSession()
    }

    override fun drawRoute(routes: List<NavigationRoute>) {
        mapboxNavigation.setNavigationRoutes(
            routes
        )
        complete = false
        mapboxNavigation.startTripSession(false)
        // RouteLine: Hiding the alternative routes when navigation starts.
        getMapboxMap().getStyle()?.apply {
            routeLineView.hideAlternativeRoutes(this)
        }
    }

//    override fun updateSetting(locationComponentPlugin: LocationComponentPlugin) {
////        locationComponentPlugin.updateSettings {
////            this.enabled = true
////            this.locationPuck = LocationPuck2D(
////                bearingImage = AppCompatResources.getDrawable(
////                    context,
//////                    R.mipmap.mapbox_mylocation_icon_default
////                    com.mapbox.navigation.ui.maps.R.drawable.mapbox_navigation_puck_icon
////                ),
////            )
////        }
//    }

    fun bind(m: List<Maneuver>?) {
//        m ?: return
//        val first = m.first()
//        var total = 0.0
//        m.forEach {
//            total += it.stepDistance.totalDistance
//        }
//        infoView.totalDistance.text = distanceFormatter.formatDistance(total).toString()
//        when (first.primary.modifier) {
//            "left", "sharp left" -> {
//                infoView.ivTurn.setImageResource(R.mipmap.ic_turn_left)
//                infoView.turnTv.text = resources.getString(R.string.turn_left)
//
//            }
//            "right", "sharp right" -> {
//                infoView.ivTurn.setImageResource(R.mipmap.ic_info_right)
//                infoView.turnTv.text = resources.getString(R.string.turn_right)
//            }
//            "uturn" -> {
//                infoView.ivTurn.setImageResource(R.mipmap.ic_navigation_uturn)
//                infoView.turnTv.text = resources.getString(R.string.turn_u)
//            }
//        }
//        infoView.turnTvMili.text =
//            first.stepDistance.distanceFormatter.formatDistance(first.stepDistance.totalDistance)
    }

    /**
     ** 通过 southwest、northeast 两个点构建 BoundingBox 对象
     **/
    fun MultiPoint.createBoundingBoxFromPoints(
        MAX_LATITUDE: Double, MAX_LONGITUDE: Double,
        MIN_LATITUDE: Double, MIN_LONGITUDE: Double
    ): BoundingBox? {
        val coordinates = coordinates()
        if (coordinates.size > 1) {
            var minLat: Double = MAX_LATITUDE
            var minLon: Double = MAX_LONGITUDE
            var maxLat: Double = MIN_LATITUDE
            var maxLon: Double = MIN_LONGITUDE
            for (gp in coordinates) {
                val latitude: Double = gp.latitude()
                val longitude: Double = gp.longitude()
                minLat = Math.min(minLat, latitude)
                minLon = Math.min(minLon, longitude)
                maxLat = Math.max(maxLat, latitude)
                maxLon = Math.max(maxLon, longitude)
            }
            val southwest = Point.fromLngLat(minLon, minLat)
            val northeast = Point.fromLngLat(maxLon, maxLat)
            return BoundingBox.fromPoints(southwest, northeast)
        }
        return null
    }

    /**
     ** 扩展BoundingBox getCenter()方法。
     **/
    fun BoundingBox.getCenter(): Point {
        val centerLon = (southwest().longitude() + northeast().longitude()) / 2.0
        val centerLat = (southwest().latitude() + northeast().latitude()) / 2.0
        return Point.fromLngLat(centerLon, centerLat)
    }


}